/*****************************************************************************
 *   Copyright 2011 ZHUANG Yuan              	                             *
 *   SudokuITS as Final Year Project for BScCS, City University of Hong Kong *
 *                                      									 *
 *   Licensed under the Apache License, Version 2.0 (the "License");         *
 *   you may not use this file except in compliance with the License.        *
 *   You may obtain a copy of the License at                                 *
 *                                                                           *
 *   http://www.apache.org/licenses/LICENSE-2.0                              *
 *                                                                           *
 *   Unless required by applicable law or agreed to in writing, software     *
 *   distributed under the License is distributed on an "AS IS" BASIS,       *
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.*
 *   See the License for the specific language governing permissions and     *
 *   limitations under the License.                                          *
 *****************************************************************************/

package android.sudoku.its.tutor;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import android.content.Context;
import android.sudoku.its.expert.Expert;
import android.sudoku.its.expert.Strategies;
import android.sudoku.its.expert.StrategiesUtil;
import android.sudoku.its.util.GameVariables;
import android.sudoku.its.views.GamePage;
import android.util.Log;

/**
 * Give detailed explaination to each strategy
 * @author 51158282
 *
 */
public class StrategyExplainer {
	private TutorUtil tutorUtil;
	private StrategiesUtil strategiesUtil;
	
	public StrategyExplainer() {
		tutorUtil = new TutorUtil();
		strategiesUtil = new StrategiesUtil();
	}
	
	/**
	 * Give detailed explanation on Full House strategy
	 * @param rowIndex		Row index of the cell to be explained
	 * @param colIndex		Column index of the cell to be explained	
	 * @param clues			An 2D array containing all the clues of the puzzle
	 * @return				An string containing explanation of Full House strategy
	 */
	protected String explainFullHouse(int rowIndex, int colIndex, int[][] clues) {
		String msg = " already contains 8 numbers. " + 
				"Only one number hasn't appeared in this row yet. \n" + 
				"Can you try to find that number by using Full House strategy?";
		
		// Check the row containing the target cell
		int count = 0; // count the number of determined cells in this row
		for (int col = 0; col < GameVariables.HOUSE_SIZE; col++) {
			if (clues[rowIndex][col] != 0) // if this is a determined cell
				count ++;
			
			if (count == Strategies.FULL_HOUSE_COUNT)  // if there is 8 determined cells for this row, full house is applicable
				return "Row " + tutorUtil.getRowString(rowIndex) + msg;			
		}
		
		// Check the column containing the target cell
		count = 0; // count the number of determined cells in this row
		for (int row = 0; row < GameVariables.HOUSE_SIZE; row++) {
			if (clues[row][colIndex] != 0) //if this is a determined cell
				count ++;
			
			if (count == Strategies.FULL_HOUSE_COUNT)  // if there is 8 determined cells for this row, full house is applicable
				return "Column " + tutorUtil.getColumnString(colIndex) + msg;	
		}
		
		// Check the block containing the target cell
		int blockStartRowIndex = (rowIndex/GameVariables.BLOCK_SIDE_LENGTH)*GameVariables.BLOCK_SIDE_LENGTH;
		int blockStartColIndex = (colIndex/GameVariables.BLOCK_SIDE_LENGTH)*GameVariables.BLOCK_SIDE_LENGTH;
		count = 0; // count the number of determined cells in this row

		for (int row=blockStartRowIndex; row<(blockStartRowIndex + GameVariables.BLOCK_SIDE_LENGTH); row++) 
			for (int col=blockStartColIndex; col<(blockStartColIndex + GameVariables.BLOCK_SIDE_LENGTH); col++) 
				if (clues[row][col] != 0) 
					count ++;	
	
		if (count == Strategies.FULL_HOUSE_COUNT)  // if there is 8 determined cells for this row, full house is applicable
			return "The block containg this cell " + msg;	
		
		return "Can you see Full House?";
	}
	
	/**
	 * Give detailed explanation on Naked Single strategy
	 * @param rowIndex		Row index of the cell to be explained
	 * @param colIndex		Column index of the cell to be explained
	 * @return				An string containing explanation of Naked Single strategy
	 */
	protected String explainNakedSingle(int rowIndex, int colIndex) {
		return "Row " + tutorUtil.getRowString(rowIndex) + ", Column " + tutorUtil.getColumnString(colIndex) + 
				" and Block " + tutorUtil.getBlockString(rowIndex, colIndex) + 
				" already contains 8 numbers. " + 
				"Only one number hasn't appeared in these 3 houses yet yet. \n" + 
				"Can you try to find that number by using Naked Single strategy?";
	}
	
	/**
	 * Give detailed explanation on Hidden Single strategy
	 * @param rowIndex		Row index of the cell to be explained
	 * @param colIndex		Column index of the cell to be explained
	 * @param cm			Candidate Matrix
	 * @return				An string containing explanation of Hidden Single strategy
	 */
	protected String explainHiddenSingle(int rowIndex, int colIndex, List<List<Integer>> cm) {
		String msgBegin = "Try to find out candidates for each cell in ";
		String msgMiddle1 = " by inputting pencil marks. \n" +  
				"You should be able to find one candidate, which only appears in Cell (" + 
				tutorUtil.getRowString(rowIndex) + ", " + 
				tutorUtil.getColumnString(colIndex) + ") but no other cells in ";
		String msgMiddle2 = ". Thus, Cell (" + tutorUtil.getRowString(rowIndex) + ", " + tutorUtil.getColumnString(colIndex) + ") " + 
				"is the only place you can place that candidate within ";
		String msgEnd = ". \n" + "Can you try to find that candidate by using Hidden Single strategy?";
		
		List<Integer> candidateOfTargetCell = new ArrayList<Integer>();
		// Make a copy of the target cell from Candidate Matrix
		candidateOfTargetCell = cm.get(rowIndex * GameVariables.HOUSE_SIZE + colIndex);
		
		/** Check the row containing the target cell **/
		if (strategiesUtil.isHiddenSingleApplicableFunc(cm, candidateOfTargetCell, GameVariables.ROW, rowIndex) != 0) {
			String houseString = "Row " + tutorUtil.getRowString(rowIndex);
			return  msgBegin + houseString + msgMiddle1 + houseString + msgMiddle2 + houseString + msgEnd;
		}
	    
		/** Check the column containing the target cell **/
		if (strategiesUtil.isHiddenSingleApplicableFunc(cm, candidateOfTargetCell, GameVariables.COLUMN, colIndex) != 0) {
			String houseString = "Column " + tutorUtil.getColumnString(colIndex);
			return msgBegin + houseString + msgMiddle1 + houseString + msgMiddle2 + houseString + msgEnd;
		}
		// Merge the candidates for all cells in this column into one list
		List<Integer> allCandidatesInCol = new ArrayList<Integer>();
		for (int row = 0; row < GameVariables.HOUSE_SIZE; row++) {
			allCandidatesInCol.addAll(cm.get(row * GameVariables.HOUSE_SIZE + colIndex));
		}      
	    
		/** Check the block containing the target cell **/		
	    int blockIndex = rowIndex/GameVariables.BLOCK_SIDE_LENGTH * GameVariables.BLOCK_SIDE_LENGTH + colIndex/GameVariables.BLOCK_SIDE_LENGTH;
		if (strategiesUtil.isHiddenSingleApplicableFunc(cm, candidateOfTargetCell, GameVariables.BLOCK, blockIndex) != 0) {
			String houseString = "Block " + tutorUtil.getBlockString(rowIndex, colIndex);				
			return msgBegin + houseString + msgMiddle1 + houseString + msgMiddle2 + houseString + msgEnd;
		}    
	    
		return "Can you see Hidden Single?";
	}
	
	/**
	 * Give detailed explanation on Naked Pair strategy
	 * @param rowIndex		Row index of the cell to be explained
	 * @param colIndex		Column index of the cell to be explained
	 * @param cm			Candidate Matrix
	 * @return				An string containing explanation of Naked Pair strategy
	 */
	protected String explainNakedPair(int rowIndex, int colIndex, List<List<Integer>> cm) {
		int cellIndex = rowIndex * GameVariables.HOUSE_SIZE + colIndex;
		String msgBegin = "Try to find out candidates for each cell in ";
		String msgMiddle1 = " by inputting pencil marks. \n" + "Observe that in ";
		String msgMiddle2 = ", Cell (" + tutorUtil.getRowString(rowIndex) + ", " + tutorUtil.getColumnString(colIndex) + ") and " + 
				"another cell have the same two candidates and no others. " + 
				"Thus, the two candidates can only be put into the two cells, " + 
				"which means that these two candidates can be eliminated from other cells in ";
		String msgEnd = ". \n" + "Can you try to find the other cell and eliminate these two candidats from other cells by using Naked Pair strategy?";
		
		/** Check the row containing the target cell **/
		for (int col = 0; col < GameVariables.HOUSE_SIZE; col++) {
			int index = rowIndex * GameVariables.HOUSE_SIZE + col;
			// Try to find another cell in this row that can apply Naked Pair except the target cell
			if ((index != cellIndex) && (cm.get(cellIndex).equals(cm.get(index)))) {
				String houseString = "Row " + tutorUtil.getRowString(rowIndex);
				//String pairCellString = "Cell (" + tutorUtil.getRowString(rowIndex) + ", " + tutorUtil.getColumnString(col) + ") ";
				return msgBegin + houseString + msgMiddle1 + houseString + msgMiddle2 + houseString + msgEnd;
				}
			}
		
		/** Check the column containing the target cell **/
		for (int row = 0; row < GameVariables.HOUSE_SIZE; row++)  {
			int index = row * GameVariables.HOUSE_SIZE + colIndex;
			// Try to find another cell in this column that can apply Naked Pair except the target cell
			if ((index != cellIndex) && (cm.get(cellIndex).equals(cm.get(index)))) {
				String houseString = "Column " + tutorUtil.getColumnString(colIndex);
				return msgBegin + houseString + msgMiddle1 + houseString + msgMiddle2 + houseString + msgEnd;
			}
		}
		
		/** Check the block containing the target cell **/
		int blockStartRowIndex = (rowIndex/GameVariables.BLOCK_SIDE_LENGTH)*GameVariables.BLOCK_SIDE_LENGTH;
		int blockStartColIndex = (colIndex/GameVariables.BLOCK_SIDE_LENGTH)*GameVariables.BLOCK_SIDE_LENGTH;		
		for (int row = 0; row < GameVariables.BLOCK_SIDE_LENGTH; row++) {
			for (int col = 0; col < GameVariables.BLOCK_SIDE_LENGTH; col++) {
				int index = (row + blockStartRowIndex) * GameVariables.HOUSE_SIZE + (col + blockStartColIndex);
				if ((index != cellIndex) && (cm.get(cellIndex).equals(cm.get(index)))) {
					String houseString = "Block " + tutorUtil.getBlockString(rowIndex, colIndex);
					return msgBegin + houseString + msgMiddle1 + houseString + msgMiddle2 + houseString + msgEnd;
				}
			}
		}
		
		return "Can you see Naked Pair?";
	}
	
	private List<Integer> removeCellNotBelongToTargetCell(int rowIndex, int colIndex, List<List<Integer>> cm, List<Integer> possibleCandidates) {
		int targetCellIndex = rowIndex * GameVariables.HOUSE_SIZE + colIndex;
		List<Integer> candidatesOfTargetCell = cm.get(targetCellIndex);
		for (int i = 0; i < possibleCandidates.size(); i ++) 
			if (!candidatesOfTargetCell.contains(possibleCandidates.get(i)))
				possibleCandidates.remove(new Integer(possibleCandidates.get(i)));
		
		return possibleCandidates;
	}
	
	protected String explainHiddenPair(int rowIndex, int colIndex, List<List<Integer>> cm) {
		String msgBegin = "Try to find out candidates for each cell in ";
		String msgMiddle1 = " by inputting pencil marks. \n" + "Observe that in ";
		String msgMiddle2 = ", ";			
		String msgMiddle3 = "there are two candidates that only exist in " + 
				"Cell (" + tutorUtil.getRowString(rowIndex) + ", " + tutorUtil.getColumnString(colIndex) + ") and " + 
				"another cell. " + 
				"Thus, the value of these two cells can only be between these two candidates, " + 
				"which means that other candidates can be eliminated from these two cells. \n";
		String msgEnd = "Can you try to find the other cell and eliminate other candidates from these two cells by using Hidden Pair strategy?";	
		
		/** Check row **/
		/* Create a list that contains all candidates of this house */
		List<Integer> mergedCMRow = strategiesUtil.mergeCandidateInHouse(cm, GameVariables.ROW, rowIndex);
		
		// It is only possible for a house to have more than 4 candidates to apply the Hidden Pair strategy
		if (mergedCMRow.size() > 4) {								
			/* Find the candidate that appears exactly twice in this house */
			List<Integer> possibleCandidates = strategiesUtil.findCandidateByFrequency(mergedCMRow, 2);
			
			// Eliminate candidates that does not belong to the target cell 
			possibleCandidates = removeCellNotBelongToTargetCell(rowIndex, colIndex, cm, possibleCandidates);

			/* Get the cell position of the occurrence of possible pairs */
			int possibleCandidatesSize = possibleCandidates.size(); 
			if (possibleCandidatesSize >= 2) { // if there are at least two candidates that appears exactly twice in this house				
				List<List<Integer>> allPossibleCandidatesPositions = strategiesUtil.getCandidatePosition(cm, possibleCandidates, GameVariables.ROW, rowIndex);					
				/* Compare the positions and find if there are any two of the candidates share the same positions */
				for (int i = 0; i < allPossibleCandidatesPositions.size(); i ++) {
					List<Integer> candidatePositions = allPossibleCandidatesPositions.get(i);
					if (strategiesUtil.ifHiddenPairApplicableFunc(allPossibleCandidatesPositions, candidatePositions)) {
						String houseString = "Row " + tutorUtil.getRowString(rowIndex);
						return msgBegin + houseString + msgMiddle1 + houseString + msgMiddle2 + msgMiddle3 + msgEnd;
					}			
				}
			}
		}		
		
		/** Check column **/
		/* Create a list that contains all candidates of this house */
		List<Integer> mergedCMColumn = strategiesUtil.mergeCandidateInHouse(cm, GameVariables.COLUMN, colIndex);
		
		// It is only possible for a house to have more than 4 candidates to apply the Hidden Pair strategy
		if (mergedCMColumn.size() > 4) {								
			/* Find the candidate that appears exactly twice in this house */
			List<Integer> possibleCandidates = strategiesUtil.findCandidateByFrequency(mergedCMColumn, 2);
			
			// Eliminate candidates that does not belong to the target cell 
			possibleCandidates = removeCellNotBelongToTargetCell(rowIndex, colIndex, cm, possibleCandidates);

			/* Get the cell position of the occurrence of possible pairs */
			int possibleCandidatesSize = possibleCandidates.size(); 
			if (possibleCandidatesSize >= 2) { // if there are at least two candidates that appears exactly twice in this house				
				List<List<Integer>> allPossibleCandidatesPositions = strategiesUtil.getCandidatePosition(cm, possibleCandidates, GameVariables.COLUMN, colIndex);					
				/* Compare the positions and find if there are any two of the candidates share the same positions */
				for (int i = 0; i < allPossibleCandidatesPositions.size(); i ++) {
					List<Integer> candidatePositions = allPossibleCandidatesPositions.get(i);
					if (strategiesUtil.ifHiddenPairApplicableFunc(allPossibleCandidatesPositions, candidatePositions)) {
						String houseString = "Column " + tutorUtil.getColumnString(colIndex);
						return msgBegin + houseString + msgMiddle1 + houseString + msgMiddle2 + msgMiddle3 + msgEnd;
					}						
				}
			}
		}	
		
		/** Check block **/
		/* Create a list that contains all candidates of this house */
	    int blockIndex = rowIndex/GameVariables.BLOCK_SIDE_LENGTH * GameVariables.BLOCK_SIDE_LENGTH + colIndex/GameVariables.BLOCK_SIDE_LENGTH;
		List<Integer> mergedBlock = strategiesUtil.mergeCandidateInHouse(cm, GameVariables.BLOCK, blockIndex);
		
		// It is only possible for a house to have more than 4 candidates to apply the Hidden Pair strategy
		if (mergedBlock.size() > 4) {								
			/* Find the candidate that appears exactly twice in this house */
			List<Integer> possibleCandidates = strategiesUtil.findCandidateByFrequency(mergedBlock, 2);
			
			// Eliminate candidates that does not belong to the target cell 
			possibleCandidates = removeCellNotBelongToTargetCell(rowIndex, colIndex, cm, possibleCandidates);

			/* Get the cell position of the occurrence of possible pairs */
			int possibleCandidatesSize = possibleCandidates.size(); 
			if (possibleCandidatesSize >= 2) { // if there are at least two candidates that appears exactly twice in this house				
				List<List<Integer>> allPossibleCandidatesPositions = strategiesUtil.getCandidatePosition(cm, possibleCandidates, GameVariables.BLOCK, blockIndex);					
				/* Compare the positions and find if there are any two of the candidates share the same positions */
				for (int i = 0; i < allPossibleCandidatesPositions.size(); i ++) {
					List<Integer> candidatePositions = allPossibleCandidatesPositions.get(i);
					if (strategiesUtil.ifHiddenPairApplicableFunc(allPossibleCandidatesPositions, candidatePositions)) {
						String houseString = "Block " + tutorUtil.getBlockString(rowIndex, colIndex);
						return msgBegin + houseString + msgMiddle1 + houseString + msgMiddle2 + msgMiddle3 + msgEnd;
					}						
				}
			}
		}
		
		return "Can you see Hidden Pair?";
	}
	
	protected String giveHiddenSingleResult(Context context, int rowIndex, int colIndex, List<List<Integer>> cm) {		
		String msg1 = " is the candidate that only appears in Cell (" + 
				tutorUtil.getRowString(rowIndex) + ", " + 
				tutorUtil.getColumnString(colIndex) + ") but no other cells in "; 
		String msg2 = ".\nThus, the correct value for this cell is ";
		
		List<Integer> candidateOfTargetCell = new ArrayList<Integer>();
		// Make a copy of the target cell from Candidate Matrix
		candidateOfTargetCell = cm.get(rowIndex * GameVariables.HOUSE_SIZE + colIndex);
		
		/** Check the row containing the target cell **/
		int candidateRow = strategiesUtil.isHiddenSingleApplicableFunc(cm, candidateOfTargetCell, GameVariables.ROW, rowIndex);
		if (candidateRow != 0) {
			((GamePage) context).handleAutoFillAnswer(rowIndex, colIndex, candidateRow);
			String houseString = "Row " + tutorUtil.getRowString(rowIndex);
			return Integer.toString(candidateRow) + msg1 + houseString + msg2 + Integer.toString(candidateRow) + ".";
		}
	    
		/** Check the column containing the target cell **/
		int candidateCol = strategiesUtil.isHiddenSingleApplicableFunc(cm, candidateOfTargetCell, GameVariables.COLUMN, colIndex);
		if (candidateCol != 0) {
			((GamePage) context).handleAutoFillAnswer(rowIndex, colIndex, candidateCol);
			String houseString = "Column " + tutorUtil.getColumnString(colIndex);
			return Integer.toString(candidateCol) + msg1 + houseString + msg2 + Integer.toString(candidateCol) + ".";			
		}
      
	    
		/** Check the block containing the target cell **/
	    int blockIndex = rowIndex/GameVariables.BLOCK_SIDE_LENGTH * GameVariables.BLOCK_SIDE_LENGTH + colIndex/GameVariables.BLOCK_SIDE_LENGTH;
	    int candidateBlock = strategiesUtil.isHiddenSingleApplicableFunc(cm, candidateOfTargetCell, GameVariables.BLOCK, blockIndex);
		if (candidateBlock != 0) {
			((GamePage) context).handleAutoFillAnswer(rowIndex, colIndex, candidateBlock);
			String houseString = "Block " + tutorUtil.getBlockString(rowIndex, colIndex);				
			return Integer.toString(candidateBlock) + msg1 + houseString + msg2 + Integer.toString(candidateBlock) + ".";
		}	
		
		return "Can you see Hidden Single";
	}
	
	
	protected String giveNakedPairResult(Expert expert, int rowIndex, int colIndex, List<List<Integer>> cm, int[][] clues, Tutor tutor) {
		int cellIndex = rowIndex * GameVariables.HOUSE_SIZE + colIndex;
		String candiatePairString = Integer.toString(cm.get(cellIndex).get(0)) + " and " + 
				Integer.toString(cm.get(cellIndex).get(1));		
		int[] candidatePairIntArr = {0, 0};
		for (int i = 0; i < 2; i ++)
			candidatePairIntArr[i] = cm.get(cellIndex).get(i);
		int[] cellPairIndexes = {0, 0};
		cellPairIndexes[0] = cellIndex;
		String msg = " can be eliminated from other cells in ";
		String targetCellString = "Cell (" + tutorUtil.getRowString(rowIndex) + ", " + tutorUtil.getColumnString(colIndex) + ") and ";
		String othterThantString = " other than ";
		
		/** Check the row containing the target cell **/
		for (int col = 0; col < GameVariables.HOUSE_SIZE; col++) {
			int index = rowIndex * GameVariables.HOUSE_SIZE + col;
			// Try to find another cell in this row that can apply Naked Pair except the target cell
			if ((index != cellIndex) && (cm.get(cellIndex).equals(cm.get(index)))) {				
				cellPairIndexes[1] = index;
				expert.updateExpertForNakedPair(candidatePairIntArr, cellPairIndexes, clues);
				tutor.updateNakedPairTaughtMatrix(cellPairIndexes);
				String houseString = "Row " + tutorUtil.getRowString(rowIndex);
				String pairCellString = "Cell (" + tutorUtil.getRowString(rowIndex) + ", " + tutorUtil.getColumnString(col) + ").";
				return candiatePairString + msg + houseString + othterThantString + targetCellString + pairCellString;
				}
			}
		
		/** Check the column containing the target cell **/
		for (int row = 0; row < GameVariables.HOUSE_SIZE; row++)  {
			int index = row * GameVariables.HOUSE_SIZE + colIndex;
			// Try to find another cell in this column that can apply Naked Pair except the target cell
			if ((index != cellIndex) && (cm.get(cellIndex).equals(cm.get(index)))) {
				cellPairIndexes[1] = index;
				expert.updateExpertForNakedPair(candidatePairIntArr, cellPairIndexes, clues);
				tutor.updateNakedPairTaughtMatrix(cellPairIndexes);
				String houseString = "Column " + tutorUtil.getColumnString(colIndex);
				String pairCellString = "Cell (" + tutorUtil.getRowString(row) + ", " + tutorUtil.getColumnString(colIndex) + ").";
				return candiatePairString + msg + houseString + othterThantString + targetCellString + pairCellString;
			}
		}
		
		/** Check the block containing the target cell **/
		if ((rowIndex == 2) && (colIndex == 4)) {
			Log.d("bug here", "here");
		}
		int blockStartRowIndex = (rowIndex/GameVariables.BLOCK_SIDE_LENGTH)*GameVariables.BLOCK_SIDE_LENGTH;
		int blockStartColIndex = (colIndex/GameVariables.BLOCK_SIDE_LENGTH)*GameVariables.BLOCK_SIDE_LENGTH;
		for (int row = 0; row < GameVariables.BLOCK_SIDE_LENGTH; row++) {
			for (int col = 0; col < GameVariables.BLOCK_SIDE_LENGTH; col++) {
				int index = (row + blockStartRowIndex) * GameVariables.HOUSE_SIZE + (col + blockStartColIndex);
				if ((index != cellIndex) && (cm.get(cellIndex).equals(cm.get(index)))) {
					cellPairIndexes[1] = index;
					expert.updateExpertForNakedPair(candidatePairIntArr, cellPairIndexes, clues);
					tutor.updateNakedPairTaughtMatrix(cellPairIndexes);
					String houseString = "Block " + tutorUtil.getBlockString(rowIndex, colIndex);
					String pairCellString = "Cell (" + tutorUtil.getRowString(row + blockStartRowIndex) + ", " + tutorUtil.getColumnString(col + blockStartColIndex) + ").";
					return candiatePairString + msg + houseString + othterThantString + targetCellString + pairCellString;
				}
			}
		}
		
		return "Can you see Naked Pair?";
	}
	private String giveHiddenPairResultFunc(int targetCellIndex, int rowIndex, int colIndex, List<List<Integer>> cm, List<Integer> possibleCandidates, List<Integer> candidatePositions, int[][] clues, Tutor tutor, Expert expert, int houseType) {
		String msgBegin = "Candidates other than ";
		String andString = " and ";
		String msg = " can be eliminated from ";
		String cellString = "";
		int anotherCellIndex = 0;
		int[] candidatePair = new int[2];
		int[] cellPairIndexes = new int[2];
		cellPairIndexes[0] = targetCellIndex;
		for (int j = 0; j < 2; j ++) {
			int cellIndex = 0;
			switch (houseType) {
				case GameVariables.ROW:
					cellIndex = rowIndex * GameVariables.HOUSE_SIZE + candidatePositions.get(j); 
					if (cellIndex != targetCellIndex) {
						anotherCellIndex = cellIndex;
						cellPairIndexes[1] = anotherCellIndex;
						cellString = "Cell (" + tutorUtil.getRowString(rowIndex) + ", " + tutorUtil.getColumnString(colIndex) + ") and " +
								"Cell (" + tutorUtil.getRowString(rowIndex) + ", " + tutorUtil.getColumnString(candidatePositions.get(j)) + ").";		
						}	
					break;
				case GameVariables.COLUMN:
					cellIndex = candidatePositions.get(j) * GameVariables.HOUSE_SIZE + colIndex; 
					if (cellIndex != targetCellIndex) {
						anotherCellIndex = cellIndex;
						cellPairIndexes[1] = anotherCellIndex;
						cellString = "Cell (" + tutorUtil.getRowString(rowIndex) + ", " + tutorUtil.getColumnString(colIndex) + ") and " +
								"Cell (" + tutorUtil.getRowString(candidatePositions.get(j)) + ", " + tutorUtil.getColumnString(colIndex) + ").";		
						}	
					break;
				case GameVariables.BLOCK:
					cellIndex = candidatePositions.get(j); 
					if (cellIndex != targetCellIndex) {
						anotherCellIndex = cellIndex;
						cellPairIndexes[1] = anotherCellIndex;
						cellString = "Cell (" + tutorUtil.getRowString(rowIndex) + ", " + tutorUtil.getColumnString(colIndex) + ") and " +
								"Cell (" + tutorUtil.getRowString(cellIndex/GameVariables.HOUSE_SIZE) + ", " + tutorUtil.getColumnString(cellIndex%GameVariables.HOUSE_SIZE) + ").";		
						}	
					break;
			}				
		}	
		
		Iterator<Integer> iterator = cm.get(targetCellIndex).iterator();
		int candidateCount = 0;
		for (int j = 0; j < cm.get(targetCellIndex).size(); j ++) {
			int candidate = iterator.next().intValue();
			if (possibleCandidates.contains(candidate) && cm.get(anotherCellIndex).contains(candidate)) {
				candidatePair[candidateCount] = candidate;
				candidateCount ++;
			}								
		}
		expert.updateExpertForHiddenPair(candidatePair, cellPairIndexes, clues);
		tutor.updateHiddenPairTaughtMatrix(cellPairIndexes);
		return msgBegin + Integer.toString(candidatePair[0]) + andString + Integer.toString(candidatePair[1]) + msg + cellString;
	}
	
	public String giveHiddenPairResult(Expert expert, int rowIndex, int colIndex, List<List<Integer>> cm, int[][] clues, Tutor tutor) {					
		int targetCellIndex = rowIndex * GameVariables.HOUSE_SIZE + colIndex; 
		
		/** Check row **/
		/* Create a list that contains all candidates of this house */
		List<Integer> mergedCMRow = strategiesUtil.mergeCandidateInHouse(cm, GameVariables.ROW, rowIndex);
		
		// It is only possible for a house to have more than 4 candidates to apply the Hidden Pair strategy
		if (mergedCMRow.size() > 4) {								
			/* Find the candidate that appears exactly twice in this house */
			List<Integer> possibleCandidates = strategiesUtil.findCandidateByFrequency(mergedCMRow, 2);
			
			// Eliminate candidates that does not belong to the target cell 
			possibleCandidates = removeCellNotBelongToTargetCell(rowIndex, colIndex, cm, possibleCandidates);

			/* Get the cell position of the occurrence of possible pairs */
			int possibleCandidatesSize = possibleCandidates.size(); 
			if (possibleCandidatesSize >= 2) { // if there are at least two candidates that appears exactly twice in this house				
				List<List<Integer>> allPossibleCandidatesPositions = strategiesUtil.getCandidatePosition(cm, possibleCandidates, GameVariables.ROW, rowIndex);					
				/* Compare the positions and find if there are any two of the candidates share the same positions */
				for (int i = 0; i < allPossibleCandidatesPositions.size(); i ++) {
					List<Integer> candidatePositions = allPossibleCandidatesPositions.get(i);
					if (strategiesUtil.ifHiddenPairApplicableFunc(allPossibleCandidatesPositions, candidatePositions)) {
						return giveHiddenPairResultFunc(targetCellIndex, rowIndex, colIndex, cm, possibleCandidates, candidatePositions, clues, tutor, expert, GameVariables.ROW);
					}			
				}
			}
		}		
		
		/** Check column **/
		/* Create a list that contains all candidates of this house */
		List<Integer> mergedCMColumn = strategiesUtil.mergeCandidateInHouse(cm, GameVariables.COLUMN, colIndex);
		
		// It is only possible for a house to have more than 4 candidates to apply the Hidden Pair strategy
		if (mergedCMColumn.size() > 4) {								
			/* Find the candidate that appears exactly twice in this house */
			List<Integer> possibleCandidates = strategiesUtil.findCandidateByFrequency(mergedCMColumn, 2);

			// Eliminate candidates that does not belong to the target cell 
			possibleCandidates = removeCellNotBelongToTargetCell(rowIndex, colIndex, cm, possibleCandidates);
			
			/* Get the cell position of the occurrence of possible pairs */
			int possibleCandidatesSize = possibleCandidates.size(); 
			if (possibleCandidatesSize >= 2) { // if there are at least two candidates that appears exactly twice in this house				
				List<List<Integer>> allPossibleCandidatesPositions = strategiesUtil.getCandidatePosition(cm, possibleCandidates, GameVariables.COLUMN, colIndex);					
				/* Compare the positions and find if there are any two of the candidates share the same positions */
				for (int i = 0; i < allPossibleCandidatesPositions.size(); i ++) {
					List<Integer> candidatePositions = allPossibleCandidatesPositions.get(i);
					if (strategiesUtil.ifHiddenPairApplicableFunc(allPossibleCandidatesPositions, candidatePositions)) {
						return giveHiddenPairResultFunc(targetCellIndex, rowIndex, colIndex, cm, possibleCandidates, candidatePositions, clues, tutor, expert, GameVariables.COLUMN);
					}						
				}
			}
		}
		
		/** Check block **/
		/* Create a list that contains all candidates of this house */
	    int blockIndex = rowIndex/GameVariables.BLOCK_SIDE_LENGTH * GameVariables.BLOCK_SIDE_LENGTH + colIndex/GameVariables.BLOCK_SIDE_LENGTH;
		List<Integer> mergedBlock = strategiesUtil.mergeCandidateInHouse(cm, GameVariables.BLOCK, blockIndex);
		
		// It is only possible for a house to have more than 4 candidates to apply the Hidden Pair strategy
		if (mergedBlock.size() > 4) {								
			/* Find the candidate that appears exactly twice in this house */
			List<Integer> possibleCandidates = strategiesUtil.findCandidateByFrequency(mergedBlock, 2);
			
			// Eliminate candidates that does not belong to the target cell 
			possibleCandidates = removeCellNotBelongToTargetCell(rowIndex, colIndex, cm, possibleCandidates);

			/* Get the cell position of the occurrence of possible pairs */
			int possibleCandidatesSize = possibleCandidates.size(); 
			if (possibleCandidatesSize >= 2) { // if there are at least two candidates that appears exactly twice in this house				
				List<List<Integer>> allPossibleCandidatesPositions = strategiesUtil.getCandidatePosition(cm, possibleCandidates, GameVariables.BLOCK, blockIndex);					
				/* Compare the positions and find if there are any two of the candidates share the same positions */
				for (int i = 0; i < allPossibleCandidatesPositions.size(); i ++) {
					List<Integer> candidatePositions = allPossibleCandidatesPositions.get(i);
					if (strategiesUtil.ifHiddenPairApplicableFunc(allPossibleCandidatesPositions, candidatePositions)) {
						return giveHiddenPairResultFunc(targetCellIndex, rowIndex, colIndex, cm, possibleCandidates, candidatePositions, clues, tutor, expert, GameVariables.BLOCK);
					}						
				}
			}
		}
		return "Can you see Hidden Pair?";
	}
}
